### Version History

###Restructure v.02 changes:
### Testing key events within the main game loop
### Adding transition from game loop back to shell loop

### Restructure v.01 changes
### Restructure Code as per program structure file

### v.09 changes
### separation of textobjects into a separate class
### creation of menu function to handle display of menu items

### v.08 changes
### add game over screen which displays final score, and asks the player whether to start again, or quit

### v.07 changes:
### add score and lives variables to the test function
### test the transition from the game to the game over screen

### v.06 changes:
### define a function to show the test display, and start the function when start is selected and 'enter' is pressed. 
### This test function is a placeholder for the main game function running inside the main shell loop

### v.05 changes:
### create test display to test starting the game

### v.04 changes:
### Test key events to switch selection and test quitting the game

### v.03 changes:
### introduce menumode and selected variables
### define key events to switch selection if in menumode

###  v.02 changes:
### changes text displayed on screen after a short pause
### creates new main class to initialise the game


###1. Import modules:

import pygame
from pygame.locals import *
import sys

### 2: Declare public variables
### these variables need to be accessible to various functions in various classes


screenWidth = 800
screenHeight = 600
screenSize = screenWidth, screenHeight

"""menumode keeps track of the gamestate - allowed values are: -2 - opening screen is displayed, no menu available, 0 - control map is displayed, 1 - start menu displayed, -1 - main game loop running, no menu displayed, 2 - restart menu displayed"""
menumode = -2 

"""the selected variable keeps track of which menuitem the player has selected. It can have the following values: 0 - no selectable item displayed,  1 - first/upper menuitem selected; -1 - lower/second menuitem selected"""
selected = 0

score = 0
finalScore = 0
screen = pygame.display.set_mode(screenSize)
background = pygame.Surface(screen.get_size()) #ensures background is the same size as the game window
background = background.convert()
background.fill((0, 0, 0))


### 3. Define classes

class Player(pygame.sprite.Sprite):
    pos=[400,560]
    missileX=0
    missileMoving=False
    missileY=pos[1]
    image=pygame.image.load("hero.jpg").convert()
    imagerect=image.get_rect()
##______________________ ##

    def __init__(self):
        pygame.sprite.Sprite.__init__(self) #initialise the parent class

#______________________##

    def loadImage(self,initialpos):
        
        
        self.imagerect.topleft=initialpos # set the top left corner to be equal
                                                                #to the pos value (center of the screen)
#____________________________#        

    def display(self):
        
        screen.blit(self.image,self.imagerect) #display the hero
#_____________________________#
    def moveLeft(self):
       self.pos[0]-=5                                                  # move  5 pixels to the left
       self.imagerect.topleft=(self.pos[0],self.pos[1]) #use topleft as reference
                                             #it is usually done by default but it is clearer for me
       print self.pos[0]                                             #see down for this function
       if self.imagerect.topleft==(0,self.pos[1]):       #if the hero reaches the border of the screen
           self.pos[0]=self.pos[0]+5                           #the last movement is ignored because five pixels are added
      
#_______________________________#  same as above function
    def moveRight(self):                                                
       self.pos[0]+=5
       self.imagerect.topright=(self.pos[0]+32,self.pos[1]) #using the top right corner of the image which is situated 32 pixels after the
                                                     #to the right of the left corner
       print self.pos[0]
       print self.imagerect.topright[0]

       if self.imagerect.topright[0]>=800:                       # use the corner as a stopper preventing the image from moving
           self.pos[0]=self.pos[0]-5

       
### Missile class goes here
#_____________________________# function to update screen
    def updateScreen(self): 

       screen.blit(background,(0,0)) #blit the background on the screen
                                                    #it is called after the coordinates of the ship are updated
                                                    # so the background blits on top of the old ship while a new ship appears at the new coordinates
       screen.blit(self.image,self.imagerect) #blits the new ship
       self.trajectory() 
       pygame.display.update()
     


#__________________________# draw the missiles


#having a separate function for drawing the missiles lets us decide the rate of fire
#the missileMoving condition lets me take advantage of the game loop so I do not have to create a separate loop thus hindering the game

 
    def drawMissile(self):
        
        self.missileX=self.pos[0]+15
        self.missileY=568
        pygame.display.update()
        self.missileMoving=True
        

#_________________________# moves the missile    
        
    def trajectory (self):
     if self.missileMoving: # this function only executes when the missile is moving
        self.missileY-=5
        screen.blit(missile,(self.missileX,self.missileY))




ship=Player()
missile=pygame.Surface((10,10))
missile.fill((255,0,0))
pygame.key.set_repeat(1,1)

class Enemy(pygame.sprite.Sprite):
	"""
	This class can be used to create an enemy object. Functions and notes are as follows:
	
	__init__
		defines initial variables used within the class
	
	loadimages
		loads each image for the enemy animation
		an error message will be brought up if correct images are not found
	
	update
		use RenderUpdates()
		updates animation frame each time update is called
		takes the following parameters
			currentTime	
				obtained from pygame.time.get_ticks()
			enemyMinx
				obtained from level class
			enemyMaxX
				obtained from level class
			enemyDown
				obtained from level class
			moveSpeed
				obtained from level class
	move
		controls the enemy path
		takes the following parameters
			left
				obtained from update function via enemyMinx
			right
				obtained from update function via enemyMaxX
			down
				obtained from update function via enemyDown
			speed
				obtained from update function via moveSpeed
	"""
	image = None
	
	#########################################
	#Set inital variables
	#########################################
	
	def __init__(self, initialPosition):
		pygame.sprite.Sprite.__init__(self)
	
		
		
		if Enemy.image is None:
			self.loadImages()
            # This is the first time this class has been instantiated.
            # So, load the image for this and all subsequence instances
			self.image = self.temp
			self.rect = self.image.get_rect()
			self.rect.topleft = initialPosition
			self.frame = 0
			self.nextUpdateTime = 0
			self.goingRight =  True
			self.goingLeft =  False
			self.goingDownLeft = False
			self.goingDownRight = False
			self.downTempL = 0
			self.downTempR = 0
			

	#########################################
	#loadImages Function
	#Loads images for enemy animation
	#########################################
	
	def loadImages(self):
		self.enemy1 = []
		
		try:
			#frame 1
			a1f1 = os.path.join("image","a1f1.gif")
			self.temp = pygame.image.load(a1f1).convert()
			key = self.temp.get_at((1, 1)) #find pix and use as trans colour
			self.temp.set_colorkey(key)
		
			#add frame
			self.enemy1.append(self.temp)
		
			#frame 2
			a1f2 = os.path.join("image","a1f2.gif")
			self.temp = pygame.image.load(a1f2).convert()
			key = self.temp.get_at((1, 1))
			self.temp.set_colorkey(key)
		
			#add frame
			self.enemy1.append(self.temp)
		
			#frame 3
			a1f3 = os.path.join("image","a1f3.gif")
			self.temp = pygame.image.load(a1f3).convert()
			key = self.temp.get_at((1, 1))
			self.temp.set_colorkey(key)
		
		except pygame.error:
			#elaborate error message
			self.Font = pygame.font.Font(None, 20)   
			self.textOpen = self.Font.render("Images totally cant be found. Closing...", 1, (255, 255, 255))
			self.textOpenPos = self.textOpen.get_rect()
			self.textOpenPos.centerx = background.get_rect().centerx
			self.textOpenPos.centery = background.get_rect().centery

			screen.blit(self.textOpen, self.textOpenPos)  
			pygame.display.update()
			pygame.time.delay(2000)
			sys.exit(0)
		
		#add frame
		self.enemy1.append(self.temp)
	
	#########################################
	#Update function. Used with RenderUpdates()
	#########################################
	
	def update(self, currentTime, enemyMinx, enemyMaxX, enemyDown, moveSpeed):	#pygame.time.get_ticks(), move function
		if self.nextUpdateTime < currentTime:
			#moves to the next frame, resets if there are no more
			self.frame += 1
			if self.frame >= len(self.enemy1):
				self.frame = 0
			self.image = self.enemy1[self.frame]
			self.move(enemyMinx, enemyMaxX, enemyDown, moveSpeed)

		self.nextUpdateTime = currentTime + 10
		
		
	#########################################
	#Move function
	#########################################
	
	def move(self, left, right, down, speed):
		
		for count in range(speed): #used to differ from animation speed

			#########################################
			#Which way to go
			#########################################			
			
			if self.rect.right == right - 1:
				self.goingRight = False
				self.goingLeft = False
				self.goingDownRight = True
				self.goingDownLeft = False
				
			if self.rect.left == left + 1:
				self.goingRight = False
				self.goingLeft = False
				self.goingDownRight = False
				self.goingDownLeft = True
	
			if self.downTempL == down + 1:
				self.downTempL = 0
				self.goingRight = True
				self.goingLeft = False
				self.goingDownRight = False
				self.goingDownLeft = False
			
			if self.downTempR == down + 1:
				self.downTempR = 0
				self.goingRight = False
				self.goingLeft = True
				self.goingDownRight = False
				self.goingDownLeft = False
				
			#########################################
			#Movement
			#########################################
			
			if self.goingDownLeft:
				self.rect.bottom += 1
				self.downTempL += 1
				
				
			if self.goingDownRight:
				self.rect.bottom += 1
				self.downTempR += 1

			
			if self.goingRight:
				self.rect.left +=1
			
			if self.goingLeft:
				self.rect.right -=1


class BugsMain:

        def __init__(self): 

                ##initialise pygame
                pygame.init()

        
                ## create the display
               
                pygame.display.set_caption("Bugs!")

                

                ## make the background visible and refresh
                screen.blit(background, (0, 0))
                pygame.display.flip()

        def MenuKeyMap(self):
                """This function displays the keymap to the player and waits for any key to be pressed before transition to the start/quit menu"""

                global menumode
                global selected

                screen.blit(background, (0,0))
                pygame.display.flip()


                ##initialise text objects 

                textControls = BugsText(None, 64)
                textControls.write("Controls", (0, 0, 255))
                textControls.adjustPos(0, -230)
                
                textKeyNav = BugsText(None, 36)
                textKeyNav.write("Up and Down arrows - navigate menu", (164, 164, 164))
                textKeyNav.adjustPos(0, -165)

                textEnter = BugsText(None, 36)
                textEnter.write("Enter - confirm selection", (164, 164, 164))
                textEnter.adjustPos(0, -90)

                textQuit = BugsText(None, 36)
                textQuit.write("Esc - quit", (164, 164, 164))
                textQuit.adjustPos(0, -15)                      
                
                textMove = BugsText(None, 36)
                textMove.write("Left and Right arrows - move player", (164, 164, 164))
                textMove.adjustPos(0, 60)

                textShoot = BugsText(None, 36)
                textShoot.write("Spacebar - fire missile", (164, 164, 164))
                textShoot.adjustPos(0, 135)

                textContinue = BugsText(None, 24)
                textContinue.write("Press any key to continue", (128, 128, 128))
                textContinue.adjustPos(0, 200)


                ## display text objects

                screen.blit(textControls.message, textControls.pos)
                screen.blit(textKeyNav.message, textKeyNav.pos)
                screen.blit(textEnter.message, textEnter.pos)
                screen.blit(textQuit.message, textQuit.pos)
                screen.blit(textMove.message, textMove.pos)
                screen.blit(textShoot.message, textShoot.pos)
                screen.blit(textContinue.message, textContinue.pos)
                pygame.display.flip()

                """This function waits for input from the player once all text objects are displayed"""

                



                



        def MenuStart(self):
                """This function displays the Start/Quit menu with 'Start selected and modifies the 'selected' variable accordingly"""

                global selected

                screen.blit(background, (0,0))
                pygame.display.flip()
                
                textStartS = BugsText(None, 48)
                textStartS.write("START", (255, 64, 64))
                textStartS.adjustPos(0, -50)

                textQuit = BugsText(None, 48)
                textQuit.write("QUIT",  (166, 166, 166))
                textQuit.adjustPos(0, 50)
                        
                screen.blit(textStartS.message, textStartS.pos)
                screen.blit(textQuit.message, textQuit.pos)
                pygame.display.flip()

                
                selected = 1
                return

                """This function waits for input from the player once text objects are displayed"""

                


        def MenuStartQSelect(self):
                """This function is called when the selection in the start/quit menu is reversed. 'Quit' is now displayed as selected, and the 'selected' variable modified accordingly"""

                global selected

                screen.blit(background, (0,0))
                pygame.display.flip()
                
                textStart = BugsText(None, 48)
                textStart.write("START", (166, 166, 166))
                textStart.adjustPos(0, -50)

                textQuitS = BugsText(None, 48)
                textQuitS.write("QUIT",  (255, 64, 64))
                textQuitS.adjustPos(0, 50)
                        
                screen.blit(textStart.message, textStart.pos)
                screen.blit(textQuitS.message, textQuitS.pos)
                pygame.display.flip()

                selected = -1
                return

                """This function waits for input from the player once text objects are displayed"""


        def MenuGameOver(self):
                """This function deals with the transition from the MainGame loop back to the shell loop. It displays the players final scor and asks if they want to play again."""
        
                global menumode
                global score
                global selected

                finalScore = getScore(self)

                screen.fill((0, 0, 0))
                pygame.display.flip()

                textGameOver = BugsText(None, 128)
                textGameOver.write("GAME OVER!", (255, 0, 0))
                textGameOver.adjustPos(0, 0)
                screen.blit(textGameOver.message, textGameOver.pos)
                pygame.display.flip()

                pygame.time.delay(2500)

                screen.blit(background, (0, 0))
                pygame.display.flip()


                ## get the last value of score and display it as final score
                textFinalScore = BugsText(None, 56)
                textFinalScore.write("Your final score is: " + finalScore, (0,255,0))
                textFinalScore.adjustPos(0, -75)
                
                textPlayAgain = BugsText(None, 48)
                textPlayAgain.write("Select 'Start' to play again or 'Quit' to exit", (164, 164, 164))
                textPlayAgain.adjustPos (0, 50)
                
                screen.blit(textFinalScore.message, textFinalScore.pos)
                screen.blit(textPlayAgain.message, textPlayAgain.pos)
                pygame.display.flip()

                pygame.time.delay(5000)
                menumode = 1
                selected = 1

                MainWindow.MenuStart()  
                

                        

        def MainGameLoop(self):
                """This is where the game will run inside the shell loop"""

                global menumode
                global score

                score = 0
                lives = 3

                scoreDisplay = getScore(self)

                ##this is a placeholder screen
                
                    
                screen.blit(background,(0,0))
                ship.loadImage((400,560))
                print ship.imagerect
                ship.display()

                #rectlist = enemy1.draw(screen)
                #pygame.display.update(rectlist)

                #pygame.time.delay(75)
                #enemy1.clear(screen, background)

               
                
                
                pygame.display.update()
                
                
                textScore = BugsText(None, 24)
                textScore.write("Score: " + scoreDisplay, (0, 0, 255))
                textScore.adjustPos(350, -250)

                
                screen.blit(textScore.message, textScore.pos)
                pygame.display.flip()

                while lives > 0:
                        pygame.time.delay(2500)
                        lives -= 1
                        print lives

                score = 65784


                ### end of placeholder

                """Once the variable 'lives' has reached 0 this function changes the value of menumode and uses the MenuGameOver function to display the gameover screen"""
                
                menumode = 2
                MainWindow.MenuGameOver()

        def updateAll(self):
             
                ship.updateScreen();        
                

                


        def ShellLoop(self):

                """This is where all the events in the game will run"""

                global menumode 
                global selected 

                ### initialise opening display
                textWelcome = BugsText(None, 48)
                textWelcome.write("Splat the bugs and win fabulous prizes!", (255, 0, 0))
                textWelcome.adjustPos(0, 0)
                
                screen.blit(textWelcome.message, textWelcome.pos)
                pygame.display.flip()

                pygame.time.delay(300)               

                menumode = 0
                MainWindow.MenuKeyMap()

        
                


                while 1:
                        if selected==1:
                          MainWindow.updateAll()
                        
                          if ship.missileMoving:
                            ship.trajectory()

                        for event in pygame.event.get():
                                if event.type == QUIT:
                                        return

                                elif event.type == KEYDOWN:

                                        if menumode == 0: ## pressing any key during this stage triggers MenuStart
                                                menumode = 1
                                                MainWindow.MenuStart()

                                        elif event.key == K_ESCAPE: #player chooses to exit the game
                                                return

                                        elif menumode == 1: #starting menu is displayed
                                                if event.key == K_UP:
                                                        if selected == -1: # 'quit' is currently selected,
                                                                MainWindow.MenuStart()
                                                
            
                                                if event.key == K_DOWN:
                                                        if selected == 1: # 'start' is currently selected
                                                                MainWindow.MenuStartQSelect()


                                                if event.key == K_RETURN:
                                                        if selected == -1:   ## if 'quit' is selected when return key pressed, game exits
                                                                sys.exit("bye-bye!")

                                                        elif selected == 1: ## if 'start' is selected when return key pressed, main game starts
                                                                selected = 0 #reset selected variable
                                                                menumode = -1 #set menumode to indicate that the main game is now running                
                                                                MainWindow.MainGameLoop()
                                                if event.key==K_SPACE:
                                                    if selected==1:    
                                                       ship.drawMissile()
                                                if event.key==K_LEFT:
                                                    if selected==1:
                                                      ship.moveLeft()
                                                if event.key==K_RIGHT:
                                                    
                                                    ship.moveRight()


                                        


                
                


class BugsText(pygame.font.Font):
        """This is the class that will handle text objects and their attributes"""  

        def __init__(self, font_name, size):
                pygame.font.Font.__init__(self, None, size)

        def write(self, message, color):
                self.message = self.render(message, 1, color)
                self.pos = self.message.get_rect()
   

        def adjustPos(self, adjustX, adjustY):
                self.pos.centerx = screenWidth/2 + adjustX
                self.pos.centery = screenHeight/2 +adjustY
 
        



### Sprite class goes here

### Player class goes here

### Hostiles class goes here

###  Shield class goes here



### 4. Define functions

def getScore(self):
        global score
        return(str(score))






### Start Main class


if __name__ == '__main__':
  MainWindow = BugsMain()
  MainWindow.ShellLoop()





   


